# -*- coding: utf-8 -*-
import os
import time
from datetime import date

from PIL import Image

from django.db import models
from django.db.models import ImageField, signals
from django.contrib.auth.models import User
from django.contrib.flatpages.models import FlatPage
from django.utils.safestring import mark_safe
from django.conf import settings
from django.dispatch import dispatcher
from django.conf import settings
from django.template.loader import render_to_string

import tagging
from tagging.fields import TagField

from pygameweb import markup
from utils.fields import AutoUniqueSlugField

class Project(models.Model):
    """
    stores all data for a project
    """
    # basic fields
    name = models.CharField(max_length=40, unique=True,
                    help_text="The name of your project.")
    slug = AutoUniqueSlugField(prepopulate_from='name')
    markup = models.CharField(max_length=5, choices=markup.CHOICES,
                    help_text="What markup you want to use.")
    summary = models.TextField(max_length=400,
                    help_text="A short summary what your project is about.")
    description = models.TextField(default="",
                    help_text="Describe your project here.")
    # raw html from rendered markup for caching
    raw_description = models.TextField(blank=True, editable=False)
    raw_summary = models.TextField(blank=True, editable=False)

    homepage_link = models.URLField(blank=True,
                    help_text="A link to your project website. (optional)")
    
    # delete projects without admins? or set them to inactive?
    admins = models.ManyToManyField(User, related_name="admins")
    members = models.ManyToManyField(User, blank=True, related_name="members")

    
    # ignore but keep in db
    is_inactive =  models.BooleanField(default=False,
                    help_text="Set this project inactive. (Instead of deleting it.)")
    # don't show in project listings etc. (who needs that?)
    hide =  models.BooleanField(default=False,
                    help_text=("Don't show this project on project listings " +
                               "and the recent releases sidebar"))

    # when the project was created and updated last time
    created = models.DateTimeField(auto_now_add=True)
    last_edited = models.DateTimeField(auto_now=True)
    creator = models.ForeignKey(User, related_name="project_creator")
    last_editor = models.ForeignKey(User, related_name="project_last_editor")

    # some statistics
    # number of comments for this project
    comment_count = models.PositiveIntegerField(default=0)
    
    # tagging
    tags = TagField(help_text='Comma separated, multiple word tags in quotes. '
                               + 'Example: spam,bacon,"foo bar"')

    def save(self, render_markup=True):
        """
        render markup to html before saving
        """
        if render_markup:
            self.render_markup()            
        super(Project, self).save() 
        
    def render_markup(self):
        self.raw_summary = markup.render(self.summary, self.markup)
        self.raw_description = markup.render(self.description, self.markup)
        
        
    def get_screenshot(self):
        try:
            return Screenshot.objects.filter(project=self).order_by("?")[0]
        except IndexError:
            return DummyScreenshot()
        
    def get_perms(self, user):
        if user in self.admins.all():
            return {"has_admin_perm":True, "has_member_perm":True}
        elif user in self.members.all():
            return {"has_admin_perm":False, "has_member_perm":True}
        return {"has_admin_perm":False, "has_member_perm":False}
        
    @models.permalink
    def get_absolute_url(self):
        return ("projects.views.detail", [self.slug])

    def __unicode__(self):
        return self.name

try:
    tagging.register(Project, tag_descriptor_attr="tag_objects")
except tagging.AlreadyRegistered:
    pass


class Release(models.Model):    
    project = models.ForeignKey(Project)
    version = models.CharField(max_length=20,
                   help_text="Release name or version number.")
    slug = AutoUniqueSlugField(prepopulate_from='version',
                               unique_together='project') 
    markup = models.CharField(max_length=5, choices=markup.CHOICES,
                   help_text="What markup you want to use.")
    summary = models.TextField(max_length=400,
                   help_text="A short summary of this release.")
    raw_summary = models.TextField(blank=True, editable=False)
    
    changelog = models.TextField(default="", blank=True,
                    help_text="Your changelog in detail.")
    raw_changelog = models.TextField(blank=True, editable=False)
    # dates and authors
    created = models.DateTimeField(auto_now_add=True)
    last_edited = models.DateTimeField(auto_now=True)
    creator = models.ForeignKey(User, related_name="creator")
    last_editor = models.ForeignKey(User, related_name="last_editor")
    
    # urls
    notes_url = models.URLField(blank=True,
                     help_text="A link to some external release notes.")
    download_url = models.URLField(
                     help_text="Where to download this version.")
    
    class Meta:
        unique_together = (("project", "version",),)
        ordering = ('-created',)
        
    def save(self):
        """
        render markup to html before saving
        """
        self.render_markup()
        super(Release, self).save() 
        
    def render_markup(self):
        self.raw_summary = markup.render(self.summary, self.markup)
        self.raw_changelog = markup.render(self.changelog, self.markup)

    @models.permalink
    def get_absolute_url(self):
        #return ("projects.views.detail", [self.project.slug, self.slug])
        return ("view_release", (), {"project_slug": self.project.slug,
                                     "release_slug": self.slug})
    
    def __unicode__(self):
        return u"%s %s" % (self.project, self.version)


def get_image_path(screenshot, field_attname):
    """Get upload_to path specific to this screenshot."""
    return 'screenshots/%s/%s%s' % (screenshot.project.slug, screenshot.slug, 
                                            os.path.splitext(field_attname)[1])
class Screenshot(models.Model):
    name = models.CharField(max_length=30,
             help_text="Name of this screenshot (your file will be renamed).")
    slug = AutoUniqueSlugField(prepopulate_from='name',
                               unique_together='project') 
    project = models.ForeignKey(Project)
    public =  models.BooleanField(default=True,
             help_text="If not set, only project members can see this screenshot")
    uploaded_by = models.ForeignKey(User, related_name="uploaded_by")
    
    # plaintext, no markup
    description = models.TextField(
             help_text="Describe what you can see on the screenshot. (Plaintext)")
    image = models.ImageField(upload_to=get_image_path,
             help_text="The screenshot itself.")

    class Meta:
        unique_together = (("name", "project"),)

    
    def save(self, force_insert=False, force_update=False):
        super(Screenshot, self).save(force_insert, force_update)	
        im = Image.open(os.path.join(self.image.path))
        im.thumbnail((120,93), Image.ANTIALIAS)
        thumb_path = self.image.path.replace(self.slug, 
                                             self.slug+"_thumb")
        im.save(thumb_path)        
        
        
    def __unicode__(self):
        return self.name
    
    def get_preview_url(self):
        return self.image.url.replace(self.slug, self.slug+"_thumb")
    
    def preview(self):
        url = self.get_preview_url()
        return mark_safe(u"<img src='%s' alt='screenshot for project %s'/>" % (url, self.project))
    preview.allow_tags = True
    
    @models.permalink
    def get_absolute_url(self):
        return ("projects.views.detail_screenshot", [self.project.slug, self.slug])    
    

class DummyScreenshot:
    url = settings.MEDIA_URL + settings.DUMMY_SCREENSHOT_PATH
    preview = mark_safe(u"<img src='%s' alt='no screenshot available'/>" % url)
    preview.allow_tags = True    





class Video(models.Model):
    """An externaly hosted video (currently only for youtube)"""
    name = models.CharField(max_length=30,
             help_text="Name of this screenshot (your file will be renamed).")
    slug = AutoUniqueSlugField(prepopulate_from='name',
                               unique_together='project') 
    video_id = models.CharField("YouTube video ID", max_length=50,
              help_text="the part after v= of a youtube video url, eg http://www.youtube.com/watch?v=VIDEO-ID&feature=unimportant")
    project = models.ForeignKey(Project)
    description = models.TextField(
             help_text="Describe what you can see on the video. (Plaintext)")
    added_by = models.ForeignKey(User, related_name="added_by")
    

    class Meta:
        unique_together = (("video_id", "project"),)

    def sync_with_yt(self):
        """
        get data from youtube
        """
        try:
            from gdata.youtube.service import YouTubeService
            yt = YouTubeService()
            entry = yt.GetYouTubeVideoEntry(video_id=self.video_id)
            self.name = entry.media.title.text
            self.description = entry.media.description.text
        except ImportError:
            raise
    
    def save(self, force_insert=False, force_update=False):
        if not self.id:
            self.sync_with_yt()
        super(Video, self).save(force_insert, force_update)	
        
    def __unicode__(self):
        return self.name
    
    def preview(self):
        url = self.thumb_url()
        return mark_safe(u"<img src='%s' alt='demo video for project %s'/>" % 
                         (url, self.project))
    preview.allow_tags = True
    
    @models.permalink
    def get_absolute_url(self):
        return ("projects.views.detail_video", [self.project.slug, self.slug])    

    def embed_url(self):
        return u'http://www.youtube.com/v/%s' % self.video_id

    def thumb_url(self):
        return u"http://img.youtube.com/vi/%s/default.jpg" % self.video_id 


class PollManager(models.Manager):
    def get_current(self):
        today = date.today()
        poll, created = self.get_or_create(year=today.year, month=today.month, 
                                           active=True)
        if created:
            for active_poll in self.filter(active=True):
                if active_poll is not poll:
                    active_poll.close()
        return poll
    
    def get_last(self):
        return self.filter(active=False) and self.filter(active=False)[0] or None
            
        
class Poll(models.Model):
    """
    project-of-the-month-poll
    """
    month = models.IntegerField(default=date.today().month)
    year = models.IntegerField(default=date.today().year)
    winners = models.ManyToManyField(Project, null=True)
    total_votes = models.PositiveSmallIntegerField(null=True, blank=True)
    winner_votes = models.PositiveSmallIntegerField(null=True, blank=True)
    active = models.BooleanField(default=True)
    table = models.TextField(blank=True, default="")
    
    objects = PollManager()
    
    class Meta:
        ordering = ["-year", "-month"]
    
    def __unicode__(self):
        return "Project of the month %s" % self.get_date()

    def get_date(self):
        return time.strftime("%B %Y", ((self.year, self.month,) + (0,)*7))
    
    @models.permalink    
    def get_absolute_url(self):
        return ("view_poll", [self.month, self.year])
        
    def can_vote(self, user):
        return user.is_authenticated() and not Vote.objects.filter(user=user,
                                                             poll=self).count()
                                                             
    def vote(self, user, project):
        if not self.can_vote(user):
            msg = u"You cannot vote more than once per month"
        else:
            vote = Vote(user=user, poll=self, project=project)
            vote.save()
            msg = u"You voted for %s" % project
        return msg
                
    def close(self):
        """
        close the poll, archive the result and delete related votes
        """
        self.active = False
        self.total_votes = self.vote_set.count()
        if self.total_votes:
            votes = {}
            for vote in Vote.objects.filter(poll=self):
                if vote.project in votes:
                    votes[vote.project] += 1
                else:
                    votes[vote.project] = 1
            vote.delete()
            sorted_projects = sorted(votes.iteritems(), key=lambda (k,v):(v,k),
                                     reverse=True)
            l = []
            for project, count in sorted_projects:
                if l and l[-1][0] == count:
                    l[-1][1].append(project)
                else:
                    l.append([count, [project]])
            self.winner_votes, self.winners = l[0]            
            
            table =  []
            for index, (vote_count, projects) in enumerate(l):
                for project in projects:
                    table.append({"index":index+1, "project":project, 
                        "count":vote_count, 
                        "rel_count":float(vote_count)/float(self.total_votes)*100.0})

            self.table = render_to_string('projects/pollreport.html', 
                                      { 'table': table, 'poll': self})
            self.save()
            signals.post_save.send(sender=self.__class__, instance=self, 
                                   created=True)

            

    
class Vote(models.Model):
    user = models.ForeignKey(User)
    project = models.ForeignKey(Project)
    poll = models.ForeignKey(Poll)
    
    class Meta:
        unique_together = (("user", "poll",),)
        ordering = ["-poll__year", "-poll__month", "user"]
        
    def __unicode__(self):
        return u"%s's vote for the %s" % (self.user, str(self.poll).lower())
